<!DOCTYPE html>
<html>
  <head>
    <title>
      scriptprocessornode.html
    </title>
    <script src="../../resources/testharness.js"></script>
    <script src="../../resources/testharnessreport.js"></script>
    <script src="../resources/audit-util.js"></script>
    <script src="../resources/audit.js"></script>
  </head>
  <body>
    <script id="layout-test-code">
      let audit = Audit.createTaskRunner();

      let sampleRate = 44100.0;
      let outputChannels = 6;
      let playbackTime = 0.0;

      // For the current implementation of ScriptProcessorNode, when it works
      // with OfflineAudioContext (which runs much faster than real-time) the
      // event.inputBuffer might be overwrite again before onaudioprocess ever
      // get chance to be called.  We carefully arrange the renderLengthInFrames
      // and bufferSize to have exactly the same value to avoid this issue.
      let renderLengthInFrames = 512;
      let bufferSize = 512;

      let context;

      function createBuffer(context, length) {
        let audioBuffer = context.createBuffer(2, length, sampleRate);
        let n = audioBuffer.length;
        let dataL = audioBuffer.getChannelData(0);
        let dataR = audioBuffer.getChannelData(1);

        for (let i = 0; i < n; ++i) {
          dataL[i] = -1;
          dataR[i] = 1;
        }

        return audioBuffer;
      }

      function processAudioData(event, should) {
        playbackTime = event.playbackTime;
        let expectedTime =
            context.currentTime + (bufferSize / context.sampleRate);
        let allowedTimeGap = 0.0000001;

        // There may be a little time gap which is from different thread
        // operation between currentTime when main thread fires onaudioprocess()
        // and currentTime when read in JS since currentTime is continuously
        // increasing on audio thread.

        should(playbackTime, 'playbackTime').beCloseTo(expectedTime, {
          threshold: allowedTimeGap
        });

        buffer = event.outputBuffer;
        should(buffer.numberOfChannels, 'Number of output channels')
            .beEqualTo(outputChannels);
        should(buffer.length, 'Length of buffer').beEqualTo(bufferSize);

        buffer = event.inputBuffer;
        let bufferDataL = buffer.getChannelData(0);
        let bufferDataR = buffer.getChannelData(1);

        should(bufferDataL, 'Left channel').beConstantValueOf(-1);
        should(bufferDataR, 'Right channel').beConstantValueOf(1);
      }

      function doBufferSizeTest(size, should) {
        should(() => {
          context.createScriptProcessor(size, 1, 1);
        }, 'context.createScriptProcessor(' + size + ', 1, 1)').notThrow();
      }

      audit.define(
          {label: 'test', description: 'Basic ScriptProcessorNode properties'},
          (task, should) => {
            // Create offline audio context.
            context =
                new OfflineAudioContext(2, renderLengthInFrames, sampleRate);

            should(() => {
              context.createScriptProcessor(512, 0, 0);
            }, 'createScriptProcessor(512, 0, 0)').throw();

            should(() => {
              context.createScriptProcessor(512, 1, 0);
            }, 'context.createScriptProcessor(512, 1, 0)').notThrow();

            should(() => {
              context.createScriptProcessor(512, 2, 0);
            }, 'context.createScriptProcessor(512, 2, 0)').notThrow();

            should(() => {
              context.createScriptProcessor(512, 0, 1);
            }, 'context.createScriptProcessor(512, 0, 1)').notThrow();

            should(() => {
              context.createScriptProcessor(512, 0, 2);
            }, 'context.createScriptProcessor(512, 0, 2)').notThrow();
            should(() => {
              context.createScriptProcessor(511, 1, 1);
            }, 'context.createScriptProcessor(511, 1, 1)').throw();

            doBufferSizeTest(256, should);
            doBufferSizeTest(512, should);
            doBufferSizeTest(1024, should);
            doBufferSizeTest(2048, should);
            doBufferSizeTest(4096, should);
            doBufferSizeTest(8192, should);
            doBufferSizeTest(16384, should);

            let sourceBuffer = createBuffer(context, renderLengthInFrames);

            let bufferSource = context.createBufferSource();
            bufferSource.buffer = sourceBuffer;

            let jsnode =
                context.createScriptProcessor(bufferSize, 2, outputChannels);

            bufferSource.connect(jsnode);
            jsnode.connect(context.destination);
            jsnode.onaudioprocess = event => {
              processAudioData(event, should);
            };

            bufferSource.start(0);

            context.startRendering().then(() => task.done());
            ;
          });

      audit.run();
    </script>
  </body>
</html>
